package com.eternal.export;

import android.app.Application;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.paging.PagedList;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;

import android.view.View;
import android.widget.RadioGroup;

import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.callback.NavCallback;
import com.alibaba.android.arouter.launcher.ARouter;
import com.eternal.base.concat.BluetoothEvent;
import com.eternal.base.concat.DeviceInfo;
import com.eternal.base.concat.TmpHum;
import com.eternal.base.data.HistoryRepository;
import com.eternal.base.data.RepositoryInjection;
import com.eternal.base.protocol.ProtocolTransformer;
import com.eternal.base.router.RouterActivityPath;
import com.eternal.framework.binding.command.BindingAction;
import com.eternal.framework.binding.command.BindingCommand;
import com.eternal.framework.bus.Messenger;
import com.eternal.framework.bus.RxBus;
import com.eternal.framework.component.AppManager;
import com.eternal.framework.component.BaseViewModel;
import com.eternal.framework.utils.RxUtils;
import com.eternal.framework.utils.Utils;
import com.eternal.widget.WheelView;
import com.google.common.collect.Lists;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;

import io.reactivex.Completable;
import io.reactivex.CompletableEmitter;
import io.reactivex.CompletableOnSubscribe;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;

public class ExportModel extends BaseViewModel {
    public ExportModel(@NonNull Application application) {
        super(application);
    }

    private HistoryRepository repository;
    private String mac;
    private byte port;
    public int frequency = 1;

    public int startYearNum, startDayNum, startHourNum, startMinuteNum,
            endYearNum, endDayNum, endHourNum, endMinuteNum;

    public boolean startIsAm, endIsAm;
    public ArrayList<String> months = Lists.newArrayList("JAN", "FEB", "MAR", "APR", "MAY",
            "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC");

    public MutableLiveData<String> start = new MutableLiveData<>();
    public MutableLiveData<String> end = new MutableLiveData<>();
    public MutableLiveData<Boolean> startEdit = new MutableLiveData<>();
    public MutableLiveData<Boolean> endEdit = new MutableLiveData<>();
    public MutableLiveData<Integer> startMonthNum = new MutableLiveData<>();
    public MutableLiveData<Integer> endMonthNum = new MutableLiveData<>();
    public MutableLiveData<String> time = new MutableLiveData<>();
    public MutableLiveData<Integer> timeColor = new MutableLiveData<>();

    public Disposable timeEvent;

    private TimeStore store = new TimeStore();

    private void saveStart() {
        store.startYearNum = startYearNum;
        store.startMonthNum = startMonthNum.getValue();
        store.startDayNum = startDayNum;
        store.startHourNum = startHourNum;
        store.startMinuteNum = startMinuteNum;
        store.endYearNum = endYearNum;
    }

    private void saveEnd() {
        store.endMonthNum = endMonthNum.getValue();
        store.endYearNum = endYearNum;
        store.endDayNum = endDayNum;
        store.endHourNum = endHourNum;
        store.endMinuteNum = endMinuteNum;
    }

    public void loadStart() {
        startYearNum = store.startYearNum;
        startMonthNum.setValue(store.startMonthNum);
        startDayNum = store.startDayNum;
        startHourNum = store.startHourNum;
        startMinuteNum = store.startMinuteNum;
        updateStartTime();
    }

    public void loadEnd() {
        endYearNum = store.endYearNum;
        endMonthNum.setValue(store.endMonthNum);
        endDayNum = store.endDayNum;
        endHourNum = store.endHourNum;
        endMinuteNum = store.endMinuteNum;
        updateEndTime();
    }

    public Observable<ArrayList<String>> init(final HistoryRepository repository, final String mac, final byte port, String timeStr) {
        this.repository = repository;
        this.mac = mac;
        this.port = port;
        if (RepositoryInjection.providerDeviceRepository().isConnect(mac)) {
            timeColor.setValue(ContextCompat.getColor(Utils.getContext(), android.R.color.white));
            time.setValue(Utils.getString(R.string.tip_connect,
                    ProtocolTransformer.getTime(System.currentTimeMillis())));
            registerEvent();
        } else {
            timeColor.setValue(ContextCompat.getColor(Utils.getContext(), R.color.color_FF2D55));
            time.setValue(RepositoryInjection.providerDeviceRepository().getTime(mac));
        }

        addSubscribe(RxBus.getDefault().toObservable(BluetoothEvent.class).subscribe(new Consumer<BluetoothEvent>() {
            @Override
            public void accept(BluetoothEvent event) {
                if (event.what == BluetoothEvent.DISCONNECT && mac.equals(event.obj)) {
                     Completable.create(new CompletableOnSubscribe() {
                        @Override
                        public void subscribe(CompletableEmitter emitter) throws Exception {
                            String timeStr = Utils.getString(R.string.tip_last,
                                    ProtocolTransformer.getTime(System.currentTimeMillis()));
                            time.setValue(timeStr);
                            timeColor.setValue(ContextCompat.getColor(Utils.getContext(), R.color.color_FF2D55));
                        }
                    }).subscribeOn(AndroidSchedulers.mainThread()).subscribe();
                    if(timeEvent!=null){
                        timeEvent.dispose();
                        timeEvent=null;
                    }
                } else if (event.what == BluetoothEvent.CONNECT && mac.equals(event.obj)) {
                    timeColor.setValue(ContextCompat.getColor(Utils.getContext(), android.R.color.white));
                    time.setValue(Utils.getString(R.string.tip_connect,
                            ProtocolTransformer.getTime(System.currentTimeMillis())));
                    registerEvent();
                }
            }
        }));

        return Observable.create(new ObservableOnSubscribe<ArrayList<String>>() {
            @Override
            public void subscribe(ObservableEmitter<ArrayList<String>> emitter) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTimeInMillis(repository.getFirstTime(mac));
                SimpleDateFormat format = new SimpleDateFormat("h:mm am @ MMM d yyyy", Locale.ENGLISH);
                start.postValue(format.format(calendar.getTime()));
                startYearNum = calendar.get(Calendar.YEAR);
                startDayNum = calendar.get(Calendar.DAY_OF_MONTH);
                startHourNum = calendar.get(Calendar.HOUR_OF_DAY);
                startIsAm = true;
                if (startHourNum >= 12) {
                    startHourNum -= 12;
                    startIsAm = false;
                }
                if (startHourNum == 0) {
                    startHourNum = 12;
                }
                startMinuteNum = calendar.get(Calendar.MINUTE);
                startMonthNum.postValue(calendar.get(Calendar.MONTH));
                calendar.setTimeInMillis(System.currentTimeMillis());
                end.postValue(format.format(calendar.getTime()));
                endYearNum = calendar.get(Calendar.YEAR);
                endDayNum = calendar.get(Calendar.DAY_OF_MONTH);
                endHourNum = calendar.get(Calendar.HOUR_OF_DAY);
                endIsAm = true;
                if (endHourNum >= 12) {
                    endHourNum -= 12;
                    endIsAm = false;
                }
                if (endHourNum == 0) {
                    endHourNum = 12;
                }
                endMinuteNum = calendar.get(Calendar.MINUTE);
                endMonthNum.postValue(calendar.get(Calendar.MONTH));
                emitter.onNext(createNumber(1970, 2999));
                emitter.onComplete();
            }
        });
    }

    @Override
    public void unregisterRxBus() {
        if(timeEvent!=null){
            timeEvent.dispose();
            timeEvent=null;
        }
    }

    public BindingCommand<Void> onExport = new BindingCommand<>(new BindingAction() {
        @Override
        public void call() {
            Messenger.getDefault().sendNoMsg(ExportActivity.EXPORT);
        }
    });

    public BindingCommand<Void> onBack = new BindingCommand<>(new BindingAction() {
        @Override
        public void call() {
            ARouter.getInstance().build(RouterActivityPath.Detail.PAGE_DETAIL)
                    .withTransition(R.anim.left_in, R.anim.right_out)
                    .navigation(AppManager.getAppManager().currentActivity(), new NavCallback() {
                        @Override
                        public void onArrival(Postcard postcard) {
                            finish();
                        }
                    });
        }
    });

    public RadioGroup.OnCheckedChangeListener frequencyListener = new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            if (checkedId == R.id.frequency_15) {
                frequency = 15;
            } else if (checkedId == R.id.frequency_30) {
                frequency = 30;
            } else if (checkedId == R.id.frequency_60) {
                frequency = 60;
            } else if (checkedId == R.id.frequency_720) {
                frequency = 720;
            } else if (checkedId == R.id.frequency_1440) {
                frequency = 1440;
            }
        }
    };

    public WheelView.OnSelectListener startYear = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            startYearNum = Integer.parseInt(text);
            updateStartTime();
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener startMoth = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (startEdit.getValue() == Boolean.TRUE) {
                startMonthNum.setValue(id);
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener startDay = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (startEdit.getValue() == Boolean.TRUE) {
                startDayNum = id + 1;
                updateStartTime();
            }

        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener startHour = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (startEdit.getValue() == Boolean.TRUE) {
                startHourNum = id + 1;
                updateStartTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener startMinute = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (startEdit.getValue() == Boolean.TRUE) {
                startMinuteNum = id;
                updateStartTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener startAp = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (startEdit.getValue() == Boolean.TRUE) {
                startIsAm = id == 0;
                updateStartTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener endYear = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (endEdit.getValue() == Boolean.TRUE) {
                endYearNum = Integer.parseInt(text);
                updateEndTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener endMoth = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (endEdit.getValue() == Boolean.TRUE) {
                endMonthNum.setValue(id);
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener endDay = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (endEdit.getValue() == Boolean.TRUE) {
                endDayNum = id + 1;
                updateEndTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener endHour = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (endEdit.getValue() == Boolean.TRUE) {
                endHourNum = id + 1;
                updateEndTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener endMinute = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (endEdit.getValue() == Boolean.TRUE) {
                endMinuteNum = id;
                updateEndTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public WheelView.OnSelectListener endAp = new WheelView.OnSelectListener() {
        @Override
        public void endSelect(int id, String text) {
            if (endEdit.getValue() == Boolean.TRUE) {
                endIsAm = id == 0;
                updateEndTime();
            }
        }

        @Override
        public void selecting(int id, String text) {

        }
    };

    public View.OnClickListener onStartEdit = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (startEdit.getValue() == Boolean.TRUE) {
                startEdit.setValue(false);
                Messenger.getDefault().send(true, ExportActivity.CHECK_TIME);
            } else {
                saveStart();
                startEdit.setValue(true);
            }
        }
    };

    public View.OnClickListener onEndEdit = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (endEdit.getValue() == Boolean.TRUE) {
                endEdit.setValue(false);
                Messenger.getDefault().send(false, ExportActivity.CHECK_TIME);
            } else {
                saveEnd();
                endEdit.setValue(true);
            }
        }
    };

    public LiveData<PagedList<TmpHum>> export(long start, long end) {
        Calendar calendar = Calendar.getInstance();
        LiveData<PagedList<TmpHum>> export;
        calendar.setTimeInMillis(start * 1000L);
        if (frequency == 1) {
            export = repository.getHistory(mac, start, end);
        } else if (frequency == 15) {
            int first = calendar.get(Calendar.MINUTE);
            List<String> times = Lists.newArrayListWithCapacity(4);
            times.add(format(first));
            times.add(format((first + 15) % 60));
            times.add(format((first + 30) % 60));
            times.add(format((first + 45) % 60));
            export = repository.getHistoryMin(mac, times, start, end);
        } else if (frequency == 30) {
            int first = calendar.get(Calendar.MINUTE);
            List<String> times = Lists.newArrayListWithCapacity(2);
            times.add(format(first));
            times.add(format((first + 30) % 60));
            export = repository.getHistoryMin(mac, times, start, end);
        } else if (frequency == 60) {
            int first = calendar.get(Calendar.MINUTE);
            export = repository.getHistoryMin(mac, Lists.newArrayList(format(first)), start, end);
        } else if (frequency == 720) {
            int first = calendar.get(Calendar.HOUR);
            List<String> times = Lists.newArrayListWithCapacity(2);
            times.add(format(first));
            times.add(format((first + 12) % 24));
            export = repository.getHistoryHour(mac, times, format(calendar.get(Calendar.MINUTE))
                    , start, end);
        } else {
            int first = calendar.get(Calendar.HOUR);
            export = repository.getHistoryHour(mac, Lists.newArrayList(format(first)),
                    format(calendar.get(Calendar.MINUTE)), start, end);
        }
        return export;
    }

    public final String format(int value) {
        return String.format(Locale.ENGLISH, "%02d", value);
    }

    public final ArrayList<String> createNumber(int min, int max) {
        ArrayList<String> result = Lists.newArrayListWithCapacity(max - min + 1);
        for (int i = min; i <= max; ++i) {
            result.add(String.valueOf(i));
        }
        return result;
    }

    public void updateStartTime() {
        String temp = startIsAm ? "AM" : "PM";
        start.setValue(String.format(Locale.ENGLISH, "%d:%02d %s @ %s %d %d",
                startHourNum, startMinuteNum, temp, months.get(startMonthNum.getValue()), startDayNum, startYearNum));
    }

    public void updateEndTime() {
        String temp = endIsAm ? "AM" : "PM";
        end.setValue(String.format(Locale.ENGLISH, "%d:%02d %s @ %s %d %d",
                endHourNum, endMinuteNum, temp, months.get(endMonthNum.getValue()), endDayNum, endYearNum));
    }

    public Observable<DeviceInfo> getDeviceInfo() {
        return repository.getInfo(mac)
                .toObservable();
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private void updateTime() {
        if (repository.isConnect(mac)) {
            time.setValue(Utils.getString(R.string.tip_connect,
                    ProtocolTransformer.getTime(System.currentTimeMillis())));
        }
    }

    private void registerEvent() {
        RxBus bus = RxBus.getDefault();
        timeEvent = bus.toObservable(Long.class)
                .compose(RxUtils.<Long>bindToLifecycle(getLifecycleProvider()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) {
                        time.setValue(Utils.getString(R.string.tip_connect,
                                ProtocolTransformer.getTime(aLong)));
                    }
                });

    }

    private static class TimeStore {
        public int startYearNum, startDayNum, startHourNum, startMinuteNum, startMonthNum,
                endMonthNum, endYearNum, endDayNum, endHourNum, endMinuteNum;
    }
}
